#include "td_signal.hpp"
#include "lines.hpp"
#include "tick.h"
#include <cstdint>

namespace Trade {

SignalMap sm;

#if (!STEP_BY_STEP)
int64_t
step_by_step() noexcept
{
  return (0);
}
#endif // STONE_DEBUG

static int
insert_into_map(const int64_t pos, const char* line) noexcept
{
  auto it = sm.find(pos);
  if (it == sm.end()) {
    StrVecPtr sv = std::make_shared<StrVector>();
    sv->push_back(strdup(line));
    sm.insert(std::make_pair(pos, sv));
  } else {
    StrVecPtr& sv = it->second;
    sv->push_back(strdup(line));
  }
  return (0);
}

void
clear_map() noexcept
{
  for (auto it = sm.begin(); it != sm.end();) {
    for (const auto& s : *(it->second)) {
      free(s);
    }
    (it->second)->clear();
    it = sm.erase(it);
  }
}

int
read_signal_from_file(const char* filepath, const time_t stm) noexcept
{
  FILE* fp = fopen(filepath, "r");
  if (!fp) {
    printf("Can not open %s to read, Error %d\n", filepath, errno);
    return errno;
  }

  char line[1024];
  int64_t all_no = 0, inserted = 0;
  int64_t errs = 0;
  while (fgets(line, 1024, fp)) {
    // printf("%s", line);
    ++all_no;
    Document doc;
    if (doc.Parse(line).HasParseError()) {
      printf("Error at %ld: %s\n", all_no, line);
      ++errs;
    } else {
      bool good = true;
      time_t tm = 0;
      {
        int ret = 0;
        int type = 0;
        if (!ret) {
          if (doc.HasMember("t")) {
            type = doc["t"].GetInt();
          } else if (doc.HasMember("type")) {
            type = doc["type"].GetInt();
          } else {
            printf("Error type at %s\n", line);
            ret = -1112;
          }
        }

        if (!ret && (type < 0 || type > 79)) {
          printf("Error type at %s\n", line);
          ret = -1114;
        }

        if (!ret) {
          if (doc.HasMember("i")) {
            tm = time_t(doc["i"].GetInt64());
          } else {
            ret = -1113;
          }
        }
        good = (!ret);
      }

      if (good) {
        insert_into_map(tm, line);
        ++inserted;
      }
    }
  }

  fclose(fp);

  size_t k = 0;
  for (auto it = sm.cbegin(); it != sm.cend(); ++it) {
    k += (*(it->second)).size();
  }
  printf("Read %ld lines, Error %ld, inserted %ld, %ld in vectors\n",
         all_no,
         errs,
         inserted,
         k);

  return (errs);
}

void
output_map() noexcept
{
  for (auto it = sm.begin(); it != sm.end(); ++it) {
    printf("%ld:\n", it->first);
    for (const auto& s : *(it->second)) {
      printf("  %s\n", s);
    }
  }
}

StrVecPtr
find_strings(const int64_t pos) noexcept
{
  auto it = sm.find(pos);
  return (it == sm.end()) ? nullptr : it->second;
}

/*
{
    "type": "object",
    "properties": {
        "i": {
            "type": "integer",
            "mock": {
                "mock": "3000000"
            },
            "title": "data_index",
            "description": "数据索引 >= 2099200"
        },
        "p": {
            "type": "integer",
            "mock": {
                "mock": "1"
            },
            "title": "period",
            "description": "周期 1=日线，2=周线"
        },
        "t": {
            "type": "integer",
            "mock": {
                "mock": "1,2"
            },
            "title": "type",
            "description": "类型 1=日线, 2=周线"
        },
        "u": {
            "type": "array",
            "items": {
                "type": "number",
                "description": "价格，单位：跳"
            },
            "title": "up, 上限",
            "description": "2个，但是两个价格可能一样"
        },
        "d": {
            "type": "array",
            "items": {
                "type": "number",
                "description": "价格，单位：跳"
            },
            "title": "dn, 下限",
            "description": "2个。两个价格可能一样"
        },
        "x": {
            "type": "number",
            "title": "hp_price",
            "description": "跳价格 单位：跳"
        }
    },
    "required": [
        "p",
        "t",
        "i",
        "x",
        "u",
        "d"
    ],
    "x-apifox-orders": [
        "i",
        "p",
        "t",
        "x",
        "u",
        "d"
    ]
}

{
    "i": 3000000,
    "p": 1,
    "t": 1,
    "x": 1000,
    "u": [
        2527,
        2528.75
    ],
    "d": [
        2508.33,
        3508.33
    ]
}
*/

/*
{
    "type": "object",
    "properties": {
        "i": {
            "type": "integer",
            "mock": {
                "mock": "3000000"
            },
            "title": "data_index",
            "description": "数据索引 >= 2099200",
            "additionalProperties": false
        },
        "p": {
            "type": "integer",
            "mock": {
                "mock": "1"
            },
            "title": "period",
            "description": "周期 1=日线，2=周线",
            "additionalProperties": false
        },
        "t": {
            "type": "integer",
            "mock": {
                "mock": "1,2"
            },
            "title": "type",
            "description": "类型 1=日线, 2=周线",
            "additionalProperties": false
        },
        "u": {
            "type": "array",
            "items": {
                "type": "number",
                "description": "价格，单位：跳",
                "additionalProperties": false
            },
            "title": "up, 上限",
            "description": "2个，但是两个价格可能一样"
        },
        "d": {
            "type": "array",
            "items": {
                "type": "number",
                "description": "价格，单位：跳",
                "additionalProperties": false
            },
            "title": "dn, 下限",
            "description": "2个。两个价格可能一样"
        },
        "x": {
            "type": "number",
            "title": "hp_price",
            "description": "跳价格 单位：跳",
            "additionalProperties": false
        },
        "b": {
            "type": "object",
            "properties": {
                "t": {
                    "type": "integer",
                    "mock": {
                        "mock": "0,1"
                    },
                    "title": "是否是顶",
                    "description": "是否是顶"
                },
                "b": {
                    "type": "integer",
                    "mock": {
                        "mock": "0,1"
                    },
                    "title": "是否是底",
                    "description": "是否是底"
                },
                "o": {
                    "type": "number",
                    "title": "顶价格差",
                    "description": "当前价格 - 顶价格"
                },
                "m": {
                    "type": "number",
                    "title": "底价格差",
                    "description": "当前价格 - 底价格"
                },
                "p": {
                    "type": "number",
                    "title": "当前价格",
                    "description": "当前价格"
                }
            },
            "x-apifox-orders": [
                "t",
                "b",
                "o",
                "m",
                "p"
            ],
            "title": "顶底",
            "description": "顶底与价格",
            "required": [
                "t",
                "b",
                "o",
                "m",
                "p"
            ]
        }
    },
    "required": [
        "p",
        "t",
        "i",
        "x",
        "u",
        "d",
        "b"
    ],
    "x-apifox-orders": [
        "i",
        "p",
        "t",
        "x",
        "u",
        "d",
        "b"
    ]
}
*/

int
ParseLines(Document& doc, Lines& l, const char* line) noexcept
{
  int err = 0;
  // printf("Parse Lines %s\n", line);
  l.reset();
  if (doc.HasMember("x")) {
    l.hpx = doc["x"].GetFloat();
  } else {
    err = (-3);
  }
  if (doc.HasMember("i")) {
    l.idx = doc["i"].GetInt64();
  } else {
    err = (-4);
  }
  if ((!err) && doc.HasMember("u")) {
    const auto& array = doc["u"].GetArray();
    if (array.Size() == 2) {
      l.hp_up_1 = array[0].GetFloat();
      l.hp_up_2 = array[1].GetFloat();
      l.nup = (l.hp_up_1 == l.hp_up_2) ? 1 : 2;
    } else {
      err = (-2);
    }
  } else {
    err = (-20);
  }
  if ((!err) && doc.HasMember("d")) {
    const auto& array = doc["d"].GetArray();
    if (array.Size() == 2) {
      l.hp_dn_1 = array[0].GetFloat();
      l.hp_dn_2 = array[1].GetFloat();
      l.ndn = (l.hp_dn_1 == l.hp_dn_2) ? 1 : 2;
    } else {
      err = (-1);
    }
  } else {
    err = (-30);
  }

  if ((!err) && doc.HasMember("b")) {
    const auto ext_rgn = doc["b"].GetObj();
    if (ext_rgn.HasMember("t")) {
      l.ext_rgn.top = ext_rgn["t"].GetInt();
    } else {
      err = (-41);
    }
    if (ext_rgn.HasMember("b")) {
      l.ext_rgn.btm = ext_rgn["b"].GetInt();
    } else {
      err = (-42);
    }
    if (ext_rgn.HasMember("o")) {
      l.ext_rgn.hp_A = ext_rgn["o"].GetFloat();
    } else {
      err = (-43);
    }
    if (ext_rgn.HasMember("m")) {
      l.ext_rgn.hp_V = ext_rgn["m"].GetFloat();
    } else {
      err = (-44);
    }
    if (ext_rgn.HasMember("p")) {
      l.ext_rgn.hpx = ext_rgn["p"].GetFloat();
    } else {
      err = (-45);
    }
  } else {
    err = (-40);
  }

  // Do not parse SGP
  // if (doc.HasMember("b")) {
  //   const auto box = doc["b"].GetObj();
  //   if (box.HasMember("q")) {
  //     ls.sgp.got_g1_up = true;
  //     if (box.HasMember("w")) {
  //       ls.sgp.g1_up_max = box["w"].GetFloat();
  //     }
  //     if (box.HasMember("e")) {
  //       ls.sgp.g1_up_min = box["e"].GetFloat();
  //     }
  //   }
  //   if (box.HasMember("y")) {
  //     ls.sgp.got_g2_up = true;
  //     if (box.HasMember("u")) {
  //       ls.sgp.g2_up_max = box["u"].GetFloat();
  //     }
  //     if (box.HasMember("i")) {
  //       ls.sgp.g2_up_min = box["i"].GetFloat();
  //     }
  //   }
  //   if (box.HasMember("a")) {
  //     ls.sgp.got_g1_dn = true;
  //     if (box.HasMember("s")) {
  //       ls.sgp.g1_dn_max = box["s"].GetFloat();
  //     }
  //     if (box.HasMember("d")) {
  //       ls.sgp.g1_dn_min = box["d"].GetFloat();
  //     }
  //   }
  //   if (box.HasMember("h")) {
  //     ls.sgp.got_g2_dn = true;
  //     if (box.HasMember("j")) {
  //       ls.sgp.g2_dn_max = box["j"].GetFloat();
  //     }
  //     if (box.HasMember("k")) {
  //       ls.sgp.g2_dn_min = box["k"].GetFloat();
  //     }
  //   }
  // } else {
  //   return (-4);
  // }
  return err;
}

/*
{
    "type": "object",
    "properties": {
        "i": {
            "type": "integer",
            "mock": {
                "mock": "3000000"
            },
            "title": "data_index",
            "description": "数据索引",
            "additionalProperties": false
        },
        "t": {
            "type": "integer",
            "mock": {
                "mock": "12"
            },
            "title": "type",
            "description": "type=12",
            "additionalProperties": false
        },
        "o": {
            "type": "array",
            "items": {
                "type": "integer",
                "mock": {
                    "mock": "1,2,3,4,5,6,7,8,9"
                },
                "description": "定时器类型。1 stage2 多方 2. stage2 空方. 3.
stage1 多方 4. stage1 空方 5. stage2 左时间 6.stage2 右时间 7.stage1
左时间 8.stage 1 右时间 9.波动定时", "additionalProperties": false
            },
            "title": "timer_out",
            "description": "定时器超时"
        }
    },
    "x-apifox-orders": [
        "i",
        "t",
        "o"
    ],
    "required": [
        "i",
        "t",
        "o"
    ]
}

{
    "i": 3000000,
    "t": 12,
    "o": [
        1,
        3,
        5,
        7,
        8
    ]
}
*/
int
ParseTimerOut(TimerOutVector& tov, Document& doc, const char* line) noexcept
{
  // printf("Parse TimerOut %s\n", line);
  tov.clear();
  if (doc.HasMember("o")) {
    const auto& array = doc["o"].GetArray();
    for (uint32_t i = 0; i < array.Size(); ++i) {
      tov.push_back(int8_t(array[i].GetInt()));
    }
  } else {
    return (-1);
  }
  return (0);
}

/*

{
    "type": "object",
    "properties": {
        "t": {
            "type": "integer",
            "title": "Type",
            "description": "Type = 11",
            "mock": {
                "mock": "11"
            },
            "additionalProperties": false
        },
        "i": {
            "type": "integer",
            "mock": {
                "mock": "3000000"
            },
            "title": "data_index",
            "description": "数据索引 >= 2099200"
        },
        "w": {
            "type": "object",
            "properties": {
                "d": {
                    "type": "integer",
                    "mock": {
                        "mock": "-1, 0, 1"
                    },
                    "title": "dir",
                    "description": "方向",
                    "additionalProperties": false
                },
                "p": {
                    "type": "integer",
                    "mock": {
                        "mock": "-1, 0, 1"
                    },
                    "title": "prev_dir",
                    "description": "前一个方向",
                    "additionalProperties": false
                },
                "s": {
                    "type": "integer",
                    "mock": {
                        "mock": "0"
                    },
                    "title": "score_up",
                    "description": "多方得分",
                    "additionalProperties": false
                },
                "c": {
                    "type": "integer",
                    "mock": {
                        "mock": "0"
                    },
                    "title": "score_dn",
                    "description": "空房得分",
                    "additionalProperties": false
                },
                "o": {
                    "type": "integer",
                    "mock": {
                        "mock": "0"
                    },
                    "title": "score_zero_up",
                    "description": "未知方向时，多方得分",
                    "additionalProperties": false
                },
                "r": {
                    "type": "integer",
                    "mock": {
                        "mock": "0"
                    },
                    "title": "score_zero_dn",
                    "description": "未知方向时，空方得分",
                    "additionalProperties": false
                },
                "u": {
                    "type": "integer",
                    "mock": {
                        "mock": "0"
                    },
                    "title": "duration_up",
                    "description": "多方持续时长, 单位：分钟",
                    "additionalProperties": false
                },
                "z": {
                    "type": "integer",
                    "mock": {
                        "mock": "0"
                    },
                    "title": "duration_zero",
                    "description": "未知方向持续时长 单位：分钟",
                    "additionalProperties": false
                },
                "a": {
                    "type": "integer",
                    "mock": {
                        "mock": "0"
                    },
                    "title": "duration_dn",
                    "description": "空方持续时长 单位：分钟",
                    "additionalProperties": false
                }
            },
            "x-apifox-orders": [
                "d",
                "p",
                "s",
                "c",
                "o",
                "r",
                "u",
                "a",
                "z"
            ],
            "title": "wave",
            "description": "当前波动",
            "required": [
                "d",
                "p",
                "s",
                "c",
                "o",
                "r",
                "u",
                "a",
                "z"
            ]
        }
    },
    "x-apifox-orders": [
        "i",
        "t",
        "w"
    ],
    "required": [
        "t",
        "w",
        "i"
    ]
}

{
    "i": 3000000,
    "t": 11,
    "w": {
        "d": -1,
        "p": 0,
        "s": 5,
        "c": 9,
        "o": 3,
        "r": 5,
        "u": 30,
        "a": 10,
        "z": 5
    }
}
*/
int
ParseWave(Document& doc, Wave& w, const char* line) noexcept
{
  // printf("Parse Wave %s\n", line);
  w.reset();
  if (doc.HasMember("w")) {
    const auto& wave = doc["w"].GetObject();
    if (wave.HasMember("a")) {
      w.dir = dir_t(wave["a"].GetInt());
    } else
      return (-2);
    if (wave.HasMember("b"))
      w.prev_dir = dir_t(wave["b"].GetInt());
    else
      return (-3);
    if (wave.HasMember("c"))
      w.spot_dir = dir_t(wave["c"].GetInt());
    else
      return (-4);
    if (wave.HasMember("d"))
      w.solved_cls_spp = dir_t(wave["d"].GetInt());
    else
      return (-5);
    if (wave.HasMember("e"))
      w.solved_vss_spp = dir_t(wave["e"].GetInt());
    else
      return (-6);
    if (wave.HasMember("f")) {
      w.spp_cls_slope = dir_t(wave["f"].GetInt());
    } else
      return (-7);
    if (wave.HasMember("g")) {
      w.spp_vss_slope = dir_t(wave["g"].GetInt());
    } else
      return (-8);
    if (wave.HasMember("h"))
      w.spp_cls_type = dir_t(wave["h"].GetInt());
    else
      return (-9);
    if (wave.HasMember("i"))
      w.spp_vss_type = dir_t(wave["i"].GetInt());
    else
      return (-10);
    if (wave.HasMember("j"))
      w.have_cls_spp = dir_t(wave["j"].GetInt());
    else
      return (-11);
    if (wave.HasMember("k"))
      w.have_vss_spp = dir_t(wave["k"].GetInt());
    else
      return (-12);
  } else {
    return (-1);
  }
  return (0);
}

/*

{
    "type": "object",
    "properties": {
        "i": {
            "type": "integer",
            "mock": {
                "mock": "3000000"
            },
            "title": "data_index",
            "description": "数据索引",
            "additionalProperties": false
        },
        "t": {
            "type": "integer",
            "mock": {
                "mock": "10"
            },
            "title": "Type",
            "description": "Type = 10",
            "additionalProperties": false
        },
        "s": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "d": {
                        "type": "integer",
                        "mock": {
                            "mock": "-1,0,1"
                        },
                        "title": "dir",
                        "description": "方向",
                        "additionalProperties": false
                    },
                    "t": {
                        "type": "array",
                        "items": {
                            "type": "number",
                            "description": "左时间点, 时间点, 右时间点",
                            "mock": {
                                "mock": "1689181111,1689183333,1689184444"
                            },
                            "additionalProperties": false
                        },
                        "title": "时间点数组",
                        "description": "左时间点, 时间点, 右时间点"
                    }
                },
                "required": [
                    "d"
                ],
                "x-apifox-orders": [
                    "d",
                    "t"
                ],
                "description":
"方向点，至少一个点，一般是两个。一个以上，按照时间先后次序出现。"
            },
            "description": "短期预测的点",
            "title": "Stage 1"
        },
        "g": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "d": {
                        "type": "integer",
                        "mock": {
                            "mock": "-1, 0, 1"
                        },
                        "title": "方向",
                        "additionalProperties": false
                    },
                    "t": {
                        "type": "array",
                        "items": {
                            "type": "number",
                            "description": "左时间点, 时间点, 右时间点",
                            "mock": {
                                "mock": "1689187777,1689188888,1689189999"
                            },
                            "additionalProperties": false
                        },
                        "description": "左时间点, 时间点, 右时间点",
                        "title": " 时间点数组"
                    }
                },
                "x-apifox-orders": [
                    "d",
                    "t"
                ],
                "description":
"方向点，至少一个点，一般是两个。一个以上，按照时间先后次序出现。", "required":
[ "t"
                ]
            },
            "description": "长期预测的点",
            "title": "Stage 2"
        },
        "v": {
            "type": "integer",
            "mock": {
                "mock": "-1, 0, 1"
            },
            "title": "vss_dir",
            "description": "Stage 1 方向"
        },
        "e": {
            "type": "integer",
            "mock": {
                "mock": "-1, 0, 1"
            },
            "title": "ein_dir",
            "description": "Stage 2 方向"
        }
    },
    "required": [
        "t",
        "s",
        "g",
        "i",
        "v",
        "e"
    ],
    "x-apifox-orders": [
        "i",
        "t",
        "v",
        "e",
        "s",
        "g"
    ]
}

{
    "i": 3000000,
    "t": 10,
    "v": -1,
    "e": 1,
    "s": [
        {
            "d": -1,
            "t": [
                1689181111,
                1689183333,
                1689184444
            ]
        },
        {
            "d": 1,
            "t": [
                1689185555,
                1689186666,
                1689187777
            ]
        }
    ],
    "g": [
        {
            "d": -1,
            "t": [
                1689187777,
                1689188888,
                1689189999
            ]
        },
        {
            "d": -1,
            "t": [
                1689197777,
                1689198888,
                1689199999
            ]
        }
    ]
}
*/
int
ParseDirs(Document& doc, StageDirs& sd, const char* line) noexcept
{
  // printf("Parse StageDirs %s\n", line);
  auto get_timer = [](const auto& obj, TimerSpot& timers) {
    if (obj.HasMember("d"))
      timers.d = dir_t(obj["d"].GetInt());
    else
      return (-1);

    if (obj.HasMember("t")) {
      const auto& array = obj["t"].GetArray();
      if (array.Size() != 3)
        return (-3);
      timers.t[0] = time_t(array[0].GetInt64());
      timers.t[1] = time_t(array[1].GetInt64());
      timers.t[2] = time_t(array[2].GetInt64());
    } else {
      return (-2);
    }

    return (0);
  };

  // printf("Reset the sd\n");
  sd.reset();

  if (doc.HasMember("i")) {
    sd.tm = time_t(doc["i"].GetInt());
  } else {
    return (-10);
  }

  if (doc.HasMember("v")) {
    sd.mid_dir = dir_t(doc["v"].GetInt());
  } else {
    return (-7);
  }
  if (doc.HasMember("e")) {
    sd.ein_dir = dir_t(doc["e"].GetInt());
  } else {
    return (-8);
  }
  if (doc.HasMember("b")) {
    sd.big_dir = dir_t(doc["b"].GetInt());
  } else {
    return (-9);
  }

  if (doc.HasMember("s")) {
    const auto& array = doc["s"].GetArray();
    sd.n_t1 = int8_t(array.Size());
    if (sd.n_t1 > 0) {
      const auto& obj = array[0].GetObject();
      if (get_timer(obj, sd.ts_mid_l))
        return (-3);
    }
    if (sd.n_t1 > 1) {
      const auto& obj = array[1].GetObject();
      if (get_timer(obj, sd.ts_mid_r))
        return (-4);
    }

  } else {
    return (-1);
  }

  if (doc.HasMember("g")) {
    const auto& array = doc["g"].GetArray();
    sd.n_t2 = int8_t(array.Size());
    if (sd.n_t2 > 0) {
      const auto& obj = array[0].GetObject();
      if (get_timer(obj, sd.ts_ein_l))
        return (-5);
    }
    if (sd.n_t2 > 1) {
      const auto& obj = array[1].GetObject();
      if (get_timer(obj, sd.ts_ein_r))
        return (-6);
    }
  } else {
    return (-2);
  }
  return (0);
}

/*  static const char* wfw_format = "{\
\"i\":%ld,\
\"a\":%ld,\
\"b\":%ld,\
\"c\":%ld,\
\"d\":%ld,\
\"e\":%ld,\
\"f\":%ld\
}";
  char buf[512];
  snprintf(buf,
           512,
           wfw_format,
           pos_,
           wfw.vt_cls_near,
           wfw.vt_cls_far,
           wfw.vt_vss_near,
           wfw.vt_vss_far,
           wfw.max_amb_delta,
           wfw.min_g2_delta);

*/

int
ParseTick(Document& doc, Tick& tick, const char* line) noexcept
{
  int ret = 0;
  if (doc.HasMember("p")) {
    tick.price = doc["p"].GetFloat();
  } else {
    ret = -1;
  }

  if (doc.HasMember("m")) {
    tick.ftime = doc["m"].GetFloat();
  } else {
    ret = -2;
  }
  return ret;
}

int
ParseWfw(Document& doc, WaitingForWrong& wfw, const char* line) noexcept
{
  // printf("Parse WaitingForWrong %s\n", line);
  wfw.reset();

  // int64_t my_idx = 0;
  // int type = 0;
  // if (doc.HasMember("i")) {
  //   my_idx = doc["i"].GetInt64();
  // }
  // if (doc.HasMember("t")) {
  //   type = doc["t"].GetInt();
  // }
  if (doc.HasMember("a")) {
    wfw.tm_cls_near = time_t(doc["a"].GetInt64());
  } else {
    return (-1);
  }
  if (doc.HasMember("b")) {
    wfw.tm_cls_far = time_t(doc["b"].GetInt64());
  } else {
    return (-2);
  }
  if (doc.HasMember("c")) {
    wfw.tm_vss_near = time_t(doc["c"].GetInt64());
  } else {
    return (-3);
  }
  if (doc.HasMember("d")) {
    wfw.tm_vss_far = time_t(doc["d"].GetInt64());
  } else {
    return (-4);
  }
  if (doc.HasMember("e")) {
    wfw.max_amb_time = time_t(doc["e"].GetInt64());
  } else {
    return (-5);
  }
  if (doc.HasMember("f")) {
    wfw.min_g2_time = time_t(doc["f"].GetInt64());
    if (wfw.min_g2_time) {
      wfw.g2_time = time_t(std::abs(wfw.min_g2_time));
      wfw.g2_dir =
        ((wfw.min_g2_time > 0) ? 1 : ((wfw.min_g2_time == 0) ? 0 : -1));
    }
  } else {
    return (-6);
  }
  if (doc.HasMember("g")) {
    wfw.g2_type = doc["g"].GetInt();
  } else {
    return (-7);
  }
  if (doc.HasMember("j")) {
    wfw.cls_dir = dir_t(doc["j"].GetInt());
  } else {
    return (-8);
  }
  if (doc.HasMember("k")) {
    wfw.vss_dir = dir_t(doc["k"].GetInt());
  } else {
    return (-9);
  }
  if (doc.HasMember("l")) {
    wfw.g2_hpx = doc["l"].GetFloat();
  } else {
    return (-10);
  }
  if (doc.HasMember("m")) {
    auto fs = dir_t(doc["m"].GetInt());
    wfw.fs_dir = sign(fs);
    wfw.fs_type = std::abs(fs);
  } else {
    return (-11);
  }
  if (doc.HasMember("n")) {
    wfw.fs_time = time_t(doc["n"].GetInt64());
  } else {
    return (-12);
  }
  if (doc.HasMember("o")) {
    wfw.fs_hpx = time_t(doc["o"].GetFloat());
  } else {
    return (-13);
  }

  return (0);
}

int
ParseDelta(Document& doc, PriceDelta& pd) noexcept
{
  int ret = 0;
  if (doc.HasMember("d")) {
    auto a = doc["d"].GetArray();
    if (a.Size() != 15) {
      ret = -2;
    } else {
      int i = 0;
      for (auto& v : doc["d"].GetArray())
        pd[i++] = v.GetFloat();
    }
  } else {
    ret = -1;
  }

  // if (!ret)
  //   print_price_delta(pd);

  return ret;
}

int
ParseForceLevel(Document& doc, ForceLevel& fl) noexcept
{
  // {"t":79,"s":-1,"sl":-1,"snl":-1,"l":-1,"ll":-1,"lnl":-7}
  int ret = 0;
  if (doc.HasMember("s")) {
    fl.d_near_10 = sign(doc["s"].GetInt());
  } else {
    ret = -1;
  }
  if (doc.HasMember("sl")) {
    fl.l_near_10 = doc["sl"].GetInt();
  } else {
    ret = -2;
  }
  if (doc.HasMember("snl")) {
    fl.l_near_rt = doc["snl"].GetInt();
    fl.d_near_rt = sign(fl.l_near_rt);
  } else {
    ret = -3;
  }
  if (doc.HasMember("l")) {
    fl.d_far_10 = sign(doc["l"].GetInt());
  } else {
    ret = -4;
  }
  if (doc.HasMember("ll")) {
    fl.l_far_10 = doc["ll"].GetInt();
  } else {
    ret = -5;
  }
  if (doc.HasMember("lnl")) {
    fl.l_far_rt = doc["lnl"].GetInt();
    fl.d_far_rt = sign(fl.l_far_rt);
  } else {
    ret = -6;
  }

  return ret;
}

int
ParseNearFarTrend(Document& doc, NearFarTrend* nft) noexcept
{
  // {"type": 6,"try": 0,"solved": 1,"long": -1,"t1": 1707485647,"t2":
  // 1706290481,"us_type": 2}
  int ret = 0;
  dir_t d_try = 0;
  dir_t d_solve = 0;

  if (doc.HasMember("try")) {
    d_try = sign(doc["try"].GetInt());
  } else {
    ret = -1;
  }
  if (doc.HasMember("solved")) {
    d_solve = sign(doc["solved"].GetInt());
  } else {
    ret = -2;
  }
  if (doc.HasMember("long")) {
    nft->d_far = sign(doc["long"].GetInt());
  } else {
    ret = -3;
  }
  if (doc.HasMember("t1")) {
    nft->t1 = time_t(doc["t1"].GetInt64());
  } else {
    ret = -4;
  }
  if (doc.HasMember("t2")) {
    nft->t2 = time_t(doc["t2"].GetInt64());
  } else {
    ret = -5;
  }

  if (d_solve)
    nft->d_near = d_solve;
  else if (d_try)
    nft->d_near = d_try;
  else
    nft->d_near = 0;

  return ret;
}


int
ParseChannels(Document& doc, Channels& cl, MaxMinArray& mma) noexcept {
  int ret = 0;


  if (doc.HasMember("i")) {
    cl.tm = time_t(doc["i"].GetInt64());
  } else {
    return (-10);
  }

  if (doc.HasMember("s")) {
    auto slope = doc["s"].GetArray();
    if (slope.Size() != 7 * 3) {
      printf("slope size is %u\n", slope.Size());
      ret = -2;
    } else {
      int8_t j = 0;
      for (int8_t i = 0; i < 7; ++i) {
        cl.cl[i].slp_up = dir_t(slope[j++].GetInt());
        cl.cl[i].slp_dn = dir_t(slope[j++].GetInt());
        cl.cl[i].type = int8_t(slope[j++].GetInt());
      }
    }
  } else {
    ret = -1;
  }

  if (doc.HasMember("m")) {
    auto mm = doc["m"].GetArray();
    if (mm.Size() != 4 * 4) {
      printf("MMA size is %u\n", mm.Size());
      ret = -3;
    } else {
      int8_t j = 0;
      for (int8_t i = 0; i < 4; ++i) {
        mma[i].tm_max = time_t(mm[j++].GetInt64());
        mma[i].tm_min = time_t(mm[j++].GetInt64());
        mma[i].hp_max = mm[j++].GetFloat();
        mma[i].hp_min = mm[j++].GetFloat();
      }
    }
  } else {
    ret = -4;
  }

  return ret;
}

} // namespace Trade
